package com.laolei.core.rest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ValueConstants;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.laolei.core.model.Entity;
import com.laolei.core.rest.annotation.RestClient;
import com.laolei.core.rest.annotation.RestMethod;
import com.laolei.core.utils.ClasseUtils;
import com.laolei.core.utils.ListUtils;
import com.laolei.core.utils.StringUtils;

public final class AnnotationAnalysis {

	private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationAnalysis.class);
	private final static List<String> ats = ListUtils
			.<String>n().a(RequestParam.class.getTypeName(), RequestHeader.class.getTypeName(),
					RequestBody.class.getName(), CookieValue.class.getTypeName(), PathVariable.class.getTypeName())
			.to();
	private final static TypeFactory typeFactory = TypeFactory.defaultInstance();

	/**
	 * 需要解析的参数注解
	 */

	private final static Map<Method, RestClientData> cachemap = new LinkedHashMap<>();

	/**
	 * 处理注解参数,如果已缓存，就从缓存返回
	 * 
	 * @param annotation
	 * @param classannotation
	 * @return
	 */
	public static RestClientData initMethodParameter(final Method method, boolean isInterface) {
		if (cachemap.containsKey(method))
			return cachemap.get(method);
		RestClientData restData = actuator(method, isInterface);
		cachemap.put(method, restData);
		return restData;
	}

	/**
	 * 解析注解参数
	 * 
	 * @param method
	 * @return
	 */
	private static RestClientData actuator(Method method, boolean isInterface) {
		Class<?> targetClass = method.getDeclaringClass();
		String methodKey = ClasseUtils.getMethodName(method);
		RestClient clazzRequest = AnnotationUtils.findAnnotation(targetClass, RestClient.class);
		RestClient methodRequest = AnnotationUtils.findAnnotation(method, RestClient.class);
		if (clazzRequest == null && methodRequest == null)
			return null;
		String[] paramNames = ClasseUtils.getMethodParamNames(method);
		RestClientData restData = new RestClientData();
		initRequestData(restData, clazzRequest);
		initRequestData(restData, methodRequest);
		if (isInterface)
			restData.setManuallyprocessed(false);
		Type originalType = method.getGenericReturnType();
		JavaType unpackType = braided(restData.isUnpack(), originalType, restData.getPackClass());

		restData.setPath(path(restData.getPath()));
		restData.setMethodKey(methodKey);
		restData.setVariable(initParamData(method, paramNames));
		restData.setReturnType(unpackType);
		return restData;
	}

	/**
	 * 如果需要解包，包装ResponseEntity类型
	 * 
	 * @param unpack
	 * @param type
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static JavaType braided(boolean unpack, Type type, Class<? extends Entity> packClass) {
		if (!unpack)
			return typeFactory.constructType(type);
		JavaType rettype = typeFactory.constructType(type);
		return typeFactory.constructParametricType(packClass, rettype);
	}

	/**
	 * 解析方法注解参数
	 * 
	 * @param method
	 * @param paramNames
	 * @return
	 */
	private static Map<Integer, Collection<RestParam>> initParamData(Method method, String[] paramNames) {

		Annotation[][] annotations = method.getParameterAnnotations();
		Map<Integer, Map<String, RestParam>> paramData = new LinkedHashMap<>();
		for (short num = 0; num < annotations.length; num++) {
			Annotation[] annt = annotations[num];
			for (Annotation annotation : annt) {
				int index = ats.indexOf(annotation.annotationType().getTypeName());
				if (index == -1)
					continue;
				Map<String, RestParam> restParams = paramData.get(index);
				if (restParams == null)
					restParams = new LinkedHashMap<>();
				String paramName = null;
				if (paramNames != null && paramNames.length > num)
					paramName = paramNames[num];
				RestParam restParam0 = getAnnotationParam(annotation, paramName, num);
				RestParam restParam1 = restParams.get(restParam0.getName());
				if (restParam1 == null) {
					restParams.put(restParam0.getName(), restParam0);
				} else {
					restParam1.getValues().addAll(restParam0.getValues());
				}
				paramData.put(index, restParams);
			}
		}
		Map<Integer, Collection<RestParam>> restParamData = new LinkedHashMap<>();
		paramData.forEach((key, data) -> {
			restParamData.put(key, data.values());
		});
		return restParamData;
	}

	/**
	 * 获取注解的参数名字，如果没有提供注解的参数名字，返回传入默认值
	 * 
	 * @param annotation
	 * @param defaultvalue
	 * @return
	 */
	private static RestParam getAnnotationParam(Annotation annotation, String defname, short num) {
		String paramName = null;
		String defaultValue = null;
		try {
			String name = (String) AnnotationUtils.getValue(annotation, "name");
			if (StringUtils.isNotEmpty(name)) {
				paramName = name;
			} else {
				String value = (String) AnnotationUtils.getValue(annotation, "value");
				if (StringUtils.isNotEmpty(value)) {
					paramName = value.trim();
				} else {
					paramName = defname.trim();
				}
			}

			defaultValue = (String) AnnotationUtils.getValue(annotation, "defaultValue");
			if (StringUtils.isNotEmpty(defaultValue) && !ValueConstants.DEFAULT_NONE.equals(defaultValue)) {
				defaultValue = defaultValue.trim();
			}
		} catch (Exception e) {
			LOGGER.info("数据提取失败", e);
		}
		return new RestParam(paramName, defaultValue, num);
	}

	/**
	 * 获得RestClient注解参数详情
	 * 
	 * @param requestData
	 * @param request
	 */
	private static void initRequestData(RestClientData requestData, RestClient request) {
		if (request == null)
			return;
		if (!request.from().isAuto())
			requestData.setFrom(request.from().isYes());
		if (!request.cache().isAuto())
			requestData.setCache(request.cache().isYes());
		if (!request.unpack().isAuto())
			requestData.setUnpack(!request.unpack().isNo());
		if (!request.manuallyprocessed().isAuto())
			requestData.setManuallyprocessed(!request.manuallyprocessed().isNo());
		if (request.method() != RestMethod.AUTO) {
			HttpMethod method = HttpMethod.resolve(request.method().method());
			requestData.setMethod(method);
		}
		if (!Entity.class.equals(request.packClass()))
			requestData.setPackClass(request.packClass());
		if (StringUtils.isNotEmpty(request.path()))
			requestData.addPath(request.path());
		if (StringUtils.isNotEmpty(request.version()))
			requestData.setVersion(request.version());
		if (StringUtils.isNotEmpty(request.serverid()))
			requestData.setServerid(request.serverid());
		if (request.cacheTimeout() != -1)
			requestData.setCachetimeout(request.cacheTimeout());
	}

	/**
	 * 格式化请求url
	 * 
	 * @param urlpath
	 * @return
	 */
	private static String path(String urlpath) {
		if (StringUtils.isEmpty(urlpath))
			return "";
		urlpath = urlpath.trim();
		String[] urlpaths = urlpath.split("\\?");
		String burlpath = "/" + urlpaths[0];
		int bindex = burlpath.length();
		burlpath = burlpath.replaceAll("/{2,}", "/");
		if (urlpaths.length == 1)
			return burlpath;
		return burlpath + urlpath.substring(bindex);
	}
}
